#ifdef HAS_LIB_YANG
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include <map>
#include <cstring>
#include <libyang/libyang.h>

using namespace std;

map<string, struct lyd_node *> g_trees;

void test_trees_print()
{   
    cout << g_trees.size() << endl;
    for (const auto& pair : g_trees) {
        char *str = NULL;
        lyd_print_mem(&str, pair.second, LYD_JSON, LYS_PRINT_SHRINK);
        printf("%s LYD_JSON:\r\n%s\r\n", pair.first.c_str(), str);
        free(str);
    
        lyd_print_mem(&str, pair.second, LYD_XML, LYS_PRINT_SHRINK);
        printf("%s LYD_XML:\r\n%s\r\n", pair.first.c_str(), str);
        free(str);
    }
}

struct lyd_node *test_get_tree_by_module(const char *module)
{
    map<string, struct lyd_node *>::iterator iter;
    iter = g_trees.find(module);
    if(iter != g_trees.end()) {
        return iter->second;
    }
    return NULL;
}

int test_tree_add_node(const struct ly_ctx *ctx, const char *xpath, const char *value)
{
    const struct lysc_node *node_lysc = lys_find_path(ctx, NULL, xpath, 0);
    if (!node_lysc) {
        return -1;
    }
    struct lyd_node *root = test_get_tree_by_module(node_lysc->module->name);
    if (!root) {
        lyd_new_path(NULL, ctx, xpath, value, LYD_NEW_PATH_UPDATE, &root);
        g_trees.insert(std::make_pair(node_lysc->module->name, root));
        return 0;
    } else {
        return lyd_new_path(root, ctx, xpath, value, LYD_NEW_PATH_UPDATE, 0);
    }
}

void test_yang_delete_xpath(struct lyd_node *root, const char *xpath)
{
    struct ly_set *set;
    lyd_find_xpath(root, xpath, &set);
    lyd_find_xpath(root, xpath, &set);
    printf("xpath=%s, count=%d\r\n", xpath, set->count);
    for (int i = 0; i < set->count; i++) {
        char *path_ =  lyd_path(set->dnodes[i], LYD_PATH_STD, NULL, 0);
        printf("delete path: %s\r\n", path_);
        free(path_);

        lyd_unlink_tree(set->dnodes[i]);
        lyd_free_all(set->dnodes[i]);
    }
    ly_set_free(set, NULL);

    char *str = NULL;
    LY_ERR _r = lyd_print_mem(&str, root, LYD_JSON, LYS_PRINT_SHRINK);
    printf("LYD_JSON-after delete %s:\r\n%s\r\n", xpath, str);
    free(str);
}

int test_tree_delete_node(const struct ly_ctx *ctx, const char *xpath)
{
    const struct lysc_node *node_lysc = lys_find_path(ctx, NULL, xpath, 0);
    if (!node_lysc) {
        return -1;
    }
    struct lyd_node *root = test_get_tree_by_module(node_lysc->module->name);
    if (root) {
        test_yang_delete_xpath(root, xpath);
    }
    return 0;
}

void test_yang_load_modules(struct ly_ctx *ctx, const char *module_name)
{
    const struct lys_module *module = ly_ctx_load_module(ctx, module_name, NULL, NULL);
    if (!module) {
        fprintf(stderr, "Failed to load module %s.\n", module_name);
        return ;
    }

    char *str = NULL;
    lys_print_mem(&str, module, LYS_OUT_TREE, 0);
    printf("\r\nyang module:\r\n%s\r\n", str);
    free(str);
}

const struct lysc_node *
find_schema_path(const struct ly_ctx *ctx, const char *schema_path)
{
    const char *end, *module_name_end;
    char *module_name = NULL;
    const struct lysc_node *node = NULL, *parent_node = NULL, *parent_node_tmp = NULL;
    const struct lys_module *module;
    size_t node_name_len;
    ly_bool found_exact_match = 0;

    /* iterate over each '/' in the path */
    while (schema_path) {
        /* example: schema_path = /listen/endpoint
         * end == NULL for endpoint, end exists for listen */
        end = strchr(schema_path + 1, '/');
        if (end) {
            node_name_len = end - schema_path - 1;
        } else {
            node_name_len = strlen(schema_path + 1);
        }

        /* ex: schema_path = /ietf-interfaces:interfaces/interface/ietf-ip:ipv4 */
        module_name_end = strchr(schema_path, ':');
        if (module_name_end && (!end || (module_name_end < end))) {
            /* only get module's name, if it is in the current scope */
            free(module_name);
            /* - 1 because module_name_end points to ':' */
            module_name = strndup(schema_path + 1, module_name_end - schema_path - 1);
            if (!module_name) {
                printf("Memory allocation failed (%s:%d, %s).", __FILE__, __LINE__, strerror(errno));
                parent_node = NULL;
                goto cleanup;
            }
            /* move the pointer to the beginning of the node's name - 1 */
            schema_path = module_name_end;

            /* recalculate the length of the node's name, because the module prefix mustn't be compared later */
            if (module_name_end < end) {
                node_name_len = end - module_name_end - 1;
            } else if (!end) {
                node_name_len = strlen(module_name_end + 1);
            }
        }

        module = ly_ctx_get_module_implemented(ctx, module_name);
        if (!module) {
            /* unknown module name */
            parent_node = NULL;
            goto cleanup;
        }

        /* iterate over the node's siblings / module's top level containers */
        while ((node = lys_getnext(node, parent_node, module->compiled, LYS_GETNEXT_WITHCASE | LYS_GETNEXT_WITHCHOICE))) {
            if (end && !strncmp(node->name, schema_path + 1, node_name_len) && (node->name[node_name_len] == '\0')) {
                /* check if the whole node's name matches and it's not just a common prefix */
                parent_node = node;
                break;
            } else if (!strncmp(node->name, schema_path + 1, node_name_len)) {
                /* do the same here, however if there is no exact match, use the last node with the same prefix */
                if (strlen(node->name) == node_name_len) {
                    parent_node = node;
                    found_exact_match = 1;
                    break;
                } else {
                    parent_node_tmp = node;
                }
            }
        }

        if (!end && !found_exact_match) {
            /* no exact match */
            parent_node = parent_node_tmp;
        }
        found_exact_match = 0;

        /* next iter */
        schema_path = strchr(schema_path + 1, '/');
    }

cleanup:
    free(module_name);
    return parent_node;
}

TEST(libyang_suite, case1) {
    
    LY_ERR ret = LY_SUCCESS;
    struct ly_ctx *ctx = NULL;
    
    // 初始化libyang上下文
    ret = ly_ctx_new("test/modules/yang", 0, &ctx);
    if (!ctx) {
        fprintf(stderr, "Failed to create context.\n");
        return ;
    }

    // 假设YANG模块文件名为 "acme-system.yang"
    test_yang_load_modules(ctx, "acme-system");
    test_yang_load_modules(ctx, "dhcp");

    /* using lysc tree */
    ly_ctx_set_options(ctx, LY_CTX_SET_PRIV_PARSED);

    // 从data path获取 lysc_node
    const struct lysc_node *node_lysc = lys_find_path(ctx, NULL, "/acme-system:system/food/tea", 0);
    printf("\r\n module:%s\r\n", node_lysc->module->name);
    // 根据lysc_node 转 schema path
    char path[4078] = {0};
    lysc_path(node_lysc, LYSC_PATH_LOG, path, 4078);
    printf("\r\nschema path=%s\r\n", path);

    // 创建树
    LY_ERR _r;
    test_tree_add_node(ctx, "/acme-system:system/host-name", "huawei");
    test_tree_add_node(ctx, "/acme-system:system/food/tea", "longjungcha");
    test_tree_add_node(ctx, "/acme-system:system/login/message", "login-jungle");
    test_tree_add_node(ctx, "/acme-system:system/login/user[name='jungle']", 0);
    test_tree_add_node(ctx, "/acme-system:system/login/user[name='jungle']/age", "32");
    test_tree_add_node(ctx, "/acme-system:system/login/user[name='koko']/age", "19");
    test_tree_add_node(ctx, "/acme-system:system/login/user[name='koko']/age", "18");
    test_tree_add_node(ctx, "/dhcp:dhcp/host-name", "S6730-H");
    test_tree_add_node(ctx, "/dhcp:dhcp/domain[.='huawei.com']", 0);
    test_tree_add_node(ctx, "/dhcp:dhcp/domain[.='baidu.com']", 0);
    test_trees_print();

    test_tree_delete_node(ctx, "/acme-system:system/login/user[name='jungle']/age");
    test_tree_delete_node(ctx, "/acme-system:system/login/user[name='koko']/age");
    test_tree_delete_node(ctx, "/acme-system:system/login");
    test_tree_delete_node(ctx, "/acme-system:system/food");
    test_tree_delete_node(ctx, "/acme-system:system/host-name");
    test_tree_delete_node(ctx, "/dhcp:dhcp/host-name");
    test_tree_delete_node(ctx, "/dhcp:dhcp/domain[.='baidu.com']");
    test_tree_delete_node(ctx, "/dhcp:dhcp/domain[.='huawei.com']");
    test_trees_print();

    ASSERT_TRUE(1);
}
#endif